package gui;

import java.awt.Color;
import java.awt.Graphics;
import java.text.DecimalFormat;
import java.text.NumberFormat;

import javax.swing.JPanel;

import universe.CelestialBody;
import universe.Universe;

public class UniversePanel2D extends JPanel {

	private static final long serialVersionUID = 1L;

	private static final long SECONDS_IN_AN_HOUR = 3600;
	private static final long SECONDS_IN_A_DAY = SECONDS_IN_AN_HOUR * 24;
	private static final long SECONDS_IN_A_YEAR = SECONDS_IN_A_DAY * 365;
	private static final Color BACKGROUND_COLOR = Color.BLACK;
	private static final double PIXELS_PER_CENTIMETER = 38;

	private int MAXIMUM_FRACTION_DIGITS = 6;
	private int scale = 20; // scale [m] = 1 cm of the screen
	private double pixelSize = scale / PIXELS_PER_CENTIMETER;
	private NumberFormat form;
	private Universe universe;
	private int xOrigin, yOrigin;

	public UniversePanel2D(Universe universe) {
		this.universe = universe;
		form = DecimalFormat.getInstance();
		form.setMaximumFractionDigits(MAXIMUM_FRACTION_DIGITS);
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.setColor(BACKGROUND_COLOR);
		g.fillRect(0, 0, getWidth(), getHeight());
		for (CelestialBody entry : universe.bodiesInUniverse()) {
			paintCelestialBody(g, entry);
		}
		drawScale(g, getWidth(), getHeight());
		drawAge(g, universe.age(), getWidth(), getHeight() - 20);
	}

	private void paintCelestialBody(Graphics g, CelestialBody o) {
		g.setColor(Color.RED);
		int x = (int) o.getPosition().x + xOrigin;
		int y = (int) o.getPosition().y + yOrigin;
		int radius = (int) o.getBoundingSphere();
		x /= pixelSize;
		y /= pixelSize;
		radius /= pixelSize;
		drawCircle(g, x, y, radius);
		g.setColor(Color.GREEN);
		drawBodyInformation(g, o, x, (y - radius));
	}

	private void drawAge(Graphics g, double senconds, int x, int y) {
		g.setColor(Color.yellow);
		int years = (int) (senconds / SECONDS_IN_A_YEAR);
		senconds -= years * SECONDS_IN_A_YEAR;

		long days = (int) (senconds / SECONDS_IN_A_DAY);
		senconds -= days * SECONDS_IN_A_DAY;

		String date = years != 0 ? years + " years - " : "";
		date += days + " days";
		g.drawString(date, x - date.length() * 6, y);
	}

	private void drawBodyInformation(Graphics g, CelestialBody o, int x, int y) {
		StringBuilder sb = new StringBuilder();
		sb.append(o.getInfo() + " - ");
		sb.append("radius: " + form.format(o.getBoundingSphere()) + " [m]"
				+ " - ");
		sb.append(form.format(o.getMass()) + " [kg]" + "  ");
		//sb.append(form.format(o.getVelocity().mod()) + " [m/s]");
		g.drawString(sb.toString(), x - sb.length() * 5, y);
	}

	private void drawCircle(Graphics g, int x, int y, int radius) {
		x -= radius;
		y -= radius;
		g.fillOval(x, y, (int) (2 * radius), (int) (2 * radius));
	}

	private void drawScale(Graphics g, int x, int y) {
		String legend = scale + " [m]";
		g.setColor(Color.CYAN);
		int space = (int) PIXELS_PER_CENTIMETER + legend.length() * 2;
		g.drawLine((int) (x - space - PIXELS_PER_CENTIMETER), y - 5, x - space,
				y - 5);
		g.drawString(legend, x - legend.length() * 6, y);
	}

	public void setScaleTo(int scale) {
		if (scale <= 0) {
			throw new IllegalArgumentException();
		}
		this.scale = scale;
		pixelSize = scale / PIXELS_PER_CENTIMETER;
	}

	public void inscreaseScale(int numberOfTimes) {
		setScaleTo(scale + numberOfTimes);
	}

	public void decreaseScale(int numberOfTimes) {
		setScaleTo(scale - numberOfTimes);
	}

	public void setOrigin(int x, int y) {
		xOrigin = x;
		yOrigin = y;
	}

	public void increaseX(int numberOfTimes) {
		setOrigin(xOrigin + numberOfTimes, yOrigin);
	}

	public void decreaseX(int numberOfTimes) {
		setOrigin(xOrigin - numberOfTimes, yOrigin);
	}

	public void increaseY(int numberOfTimes) {
		setOrigin(xOrigin, yOrigin + numberOfTimes);
	}

	public void decreaseY(int numberOfTimes) {
		setOrigin(xOrigin, yOrigin - numberOfTimes);
	}

	public int getXOrigin() {
		return xOrigin;
	}

	public int getYOrigin() {
		return yOrigin;
	}
}
